#pragma once
#include <iostream>

#include <thread>
#include <mutex>
#include <memory>
#include <atomic>

#ifndef UNIQUE_INSTANCE
#define UNIQUE_INSTANCE(CLASS_NAME) \
    friend class vtool::unique_instance<CLASS_NAME>;\
    private:\
    using _vunique_t = vtool::unique_instance<CLASS_NAME>; \
    public:\
           static std::shared_ptr<CLASS_NAME> instance()\
    {\
        return _vunique_t::create(); \
    }\
    static void destory()\
    {\
        _vunique_t::destory();\
    }\
    private:  //UNIQUE_INSTANCE is end here

#else
#error "UNIQUE_INSTANCE already defined, please check it."
#endif

namespace vtool
{
    template <typename T>
    class unique_instance
    {
        public:
        private:
            unique_instance();
            ~unique_instance();

        public:
            static std::shared_ptr<T> create();
            static void destory();

        private:
            static void deleter(T * tins);
            static std::mutex instance_mutex;
            static std::atomic<bool> _is_inited;
            static std::shared_ptr<T> instance;
    };

    ///////////////////////////////////////////////////////////////////
    //define static member variable
    template <typename T>
        std::atomic<bool>  unique_instance<T>::_is_inited(false);

    template <typename T>
        std::shared_ptr<T> unique_instance<T>::instance;

    template <typename T>
        std::mutex unique_instance<T>::instance_mutex;

    ///////////////////////////////////////////////////////////////////
    //define static member function
    template <typename T>
        std::shared_ptr<T>  unique_instance<T>::create()
        {
            //std::cout<<__FUNCTION__<<" _is_inited:"<<_is_inited<<","
            //    <<"instance:"<<!!instance<<","
            //    <<"instance.count():"<<instance.use_count()<<","
            //    <<std::endl;

            //if it is still not initilized
            if( !_is_inited )
            {
                if(!_is_inited && instance) 
                {
                    //use_count is 0, but shared_ptr is valid, it's mean static variable has been released
                    //   in this case: instance already been release, we can not used it again.
                    //   in case program coredump, here we should create instance again.
                    std::cout<<"\n\n\n\n critical error: static variable was delete,but reuse now.\n\n\n"<<std::endl;
                    auto && ret = std::shared_ptr<T>(new T(), deleter);
                    _is_inited = true;
                    return ret;
                }

                std::lock_guard<std::mutex> guard(instance_mutex);
                if( !_is_inited )
                {
                    //why need set the delete function here,because user class 
                    //deconstructor usually priavte for sigltone, so only friend
                    //class unique_instance which import by macro UNIQUE_INSTANCE
                    //so unique_instance<T> can access user private deconstructor
                    //but delete operator can not access pritvate function
                    instance = std::shared_ptr<T>(new T(), deleter);
                    _is_inited = true;
                }
            }
            return instance;
        }

    template <typename T>
        void unique_instance<T>::destory()
        {
            //if use_count gt 0, so instance must not been release,we can use it
            if(_is_inited && instance.use_count() > 0) 
            {
                std::lock_guard<std::mutex> guard(instance_mutex);
                if(_is_inited && instance.use_count() > 0)
                {
                    _is_inited = false;
                    instance.reset();
                }
            }
        }

    template <typename T>
        void unique_instance<T>::deleter(T * tins)
        {
            _is_inited = false;
            if(NULL != tins)
            {
                delete tins; 
            }
            //after here, instance is valid but it's use_count() is 0
        }
}

